#include "StdAfx.h"
#include ".\splineeditor.h"

#include <gl/gl.h>

class LinInterpHelper : public SplinePolySemantics
{
public:
	virtual void SetCurve(int nPos, int nMax, 
		double * arrLfPos, double * arrLfHeight, SplineEditCurve & curveOut) const
	{
		double lfWidth, lfHeightDiff;
		double lfConst = 0.0;
		double lfShift = 0.0;
		lfWidth = arrLfPos[nPos];
		lfHeightDiff = arrLfHeight[nPos];
		if(nPos > 0)
		{
			lfShift = arrLfPos[nPos-1];
			lfWidth -= lfShift;
			lfConst = arrLfHeight[nPos-1];
			lfHeightDiff -= lfConst; 
		}
		mdsVector<4, double> vecCoeffs;
		vecCoeffs[0] = lfConst;
		vecCoeffs[1] = lfHeightDiff;
		curveOut.SetCoeffVec(vecCoeffs);
		curveOut.ScaleWidthBy(lfWidth);
		curveOut.ShiftBy(lfShift);
	}
};

class CubicInterpHelper : public SplinePolySemantics
{
public:
	virtual void SetCurve(int nPos, int nMax, 
		double * arrLfPos, double * arrLfHeight, SplineEditCurve & curveOut) const
	{
		double lfWidth, lfHeightDiff;
		double lfConst = 0.0;
		double lfShift = 0.0;
		lfWidth = arrLfPos[nPos];
		lfHeightDiff = arrLfHeight[nPos];
		if(nPos > 0)
		{
			lfShift = arrLfPos[nPos-1];
			lfWidth -= lfShift;
			lfConst = arrLfHeight[nPos-1];
			lfHeightDiff -= lfConst; 
		}
		mdsVector<4, double> vecCoeffs;
		vecCoeffs[0] = lfConst;
		vecCoeffs[2] = 3*lfHeightDiff;
		vecCoeffs[3] = -2*lfHeightDiff;
		curveOut.SetCoeffVec(vecCoeffs);
		curveOut.ScaleWidthBy(lfWidth);
		curveOut.ShiftBy(lfShift);
	}
};

SplinePolySemantics & SplineHelpers::GetLinearInterpolator()
{
	static LinInterpHelper result;
	return result;
}


SplinePolySemantics & SplineHelpers::GetCubicInterpolator()
{
	static CubicInterpHelper result;
	return result;
}

SplineEditor::SplineEditor(void):m_nPtsUsed(1),m_pSemantics(NULL)
{
	m_pCallback = NULL;
	m_lfAspect = 1.0;
	m_pRayPicker = NULL;
	m_arrLfSegPositions[0] = 1.0;
	m_arrLfSegHeights[0] = 0.0;
	m_bFunctionsValid = false;
	m_viewInfoInternal.SetViewBack(150.0);
}

SplineEditor::~SplineEditor(void)
{
}

void SplineEditor::ValidateFunctions()
{
	if(!m_bFunctionsValid)
	{
		if(!m_pSemantics)
		{
			m_pSemantics = & (SplineHelpers::GetCubicInterpolator());
		}
		
		for(int i = 0; i < m_nPtsUsed; ++i)
		{
			m_pSemantics->SetCurve(i, m_nPtsUsed, 
				m_arrLfSegPositions, m_arrLfSegHeights, m_arrPolynomials[i]);
		}
		m_bFunctionsValid = true;
	}
}

int SplineEditor::FindNextPoint(double lfPos) const
{
	int nResult = 0;
	while(nResult < m_nPtsUsed && m_arrLfSegPositions[nResult] < lfPos)
	{
		++nResult;
	}
	return nResult;
}

int SplineEditor::InsertPt(double lfNewPos, double lfHeight)
{
	if(m_nPtsUsed >= MAX_SPLINE_EDIT_PTS)
	{
		return -1;
	}
	int nNextPoint = FindNextPoint(lfNewPos);
	int iCurrPt;
	for(iCurrPt = m_nPtsUsed; iCurrPt > nNextPoint; --iCurrPt)
	{
		m_arrLfSegPositions[iCurrPt]	=	m_arrLfSegPositions[iCurrPt-1];
		m_arrLfSegHeights[iCurrPt]		=	m_arrLfSegHeights[iCurrPt-1]; 
	}
	++m_nPtsUsed;

	m_arrLfSegPositions[iCurrPt]	= lfNewPos;
	m_arrLfSegHeights[iCurrPt]		= lfHeight;
	return iCurrPt;
}

int SplineEditor::DeletePt(int nIdxPt)
{
	if(nIdxPt < 0 || nIdxPt >= m_nPtsUsed-1)
	{
		return -1;
	}
	--m_nPtsUsed;
	for(int iCurrPt = nIdxPt; iCurrPt < m_nPtsUsed; ++iCurrPt)
	{
		m_arrLfSegPositions[iCurrPt]	=	m_arrLfSegPositions[iCurrPt+1];
		m_arrLfSegHeights[iCurrPt]		=	m_arrLfSegHeights[iCurrPt+1]; 
	}
	return nIdxPt;
}

void SplineEditor::Draw()
{
	ValidateFunctions();
	
	glPushMatrix();
	m_viewInfoInternal.SetGlViewTo();

	glColor3d(m_vecColor[0], m_vecColor[1], m_vecColor[2]);

	double lfHeight = 0.0;
	glBegin(GL_LINE_STRIP);
	
	glVertex3d(0, -75.0, -m_lfAspect*75.0);
	double lfPosNext = 0.0;
	double lfPosCurr = 0.0;
	for(int iSeg = 0; iSeg < m_nPtsUsed; ++iSeg)
	{
		lfPosNext = m_arrLfSegPositions[iSeg];
		double lfStep = (lfPosNext-lfPosCurr)*0.05;
		for(; lfPosCurr < lfPosNext; lfPosCurr += lfStep)
		{
			lfHeight = m_arrPolynomials[iSeg].EvalScaled(lfPosCurr);

			glVertex3d(0, 150.0*(lfPosCurr-0.5), m_lfAspect*150.0*(lfHeight-0.5));
		}
	} 
	glEnd();

	glPopMatrix();
	GetViewInfo();
	
}

bool SplineEditor::OnMouseMove(UINT nFlags, double lfX, double lfY )
{
	if(nFlags & (MK_LBUTTON | MK_RBUTTON))
	{
		m_bFunctionsValid = false;
		mdsMatrix<3, double> matRot;
		matRot[0][0] = 0.0;
		matRot[0][2] = 1.0;
		matRot[2][2] = 0.0;
		matRot[2][0] = -1.0;

		mdsRay<double> pickedVec;
		mdsVector<2, double> p2ScreenPos;
		p2ScreenPos[0] = 1.0-2.0*lfX; p2ScreenPos[1] = 2.0*lfY-1.0; 
		m_pRayPicker->PickRayFromScreenCoordStateless(p2ScreenPos , pickedVec);

		mdsVector<2, double> vecSplinePt;
		vecSplinePt[0] = -pickedVec.m_p3Dir[1]/pickedVec.m_p3Dir[0];
		vecSplinePt[1] = -pickedVec.m_p3Dir[2]/pickedVec.m_p3Dir[0];

		vecSplinePt[0] += 0.5;
		vecSplinePt[1] += 0.5;

		if(vecSplinePt[0] >= 1.0)
		{
			m_mouseInfo.m_bLastMouseValid = false;
			if(m_pCallback)
			{
				m_pCallback->DoCallback();
			}
			return false;
		}

		if(nFlags & MK_LBUTTON)
		{
			if(m_mouseInfo.m_bLastMouseValid && m_mouseInfo.m_nVertSelected >= 0
				&& m_mouseInfo.m_nVertSelected < m_nPtsUsed-1)
			{
				// move selected vertex
				double lfLowBound = 0.0;
				double lfHighBound = 1.0;
				if(m_mouseInfo.m_nVertSelected > 0)
				{
					lfLowBound = 
						m_arrLfSegPositions[m_mouseInfo.m_nVertSelected-1];
				}
				if(m_mouseInfo.m_nVertSelected < this->m_nPtsUsed-1)
				{
					lfHighBound = 
						m_arrLfSegPositions[m_mouseInfo.m_nVertSelected+1];
				}

				if(vecSplinePt[0] < lfLowBound && m_mouseInfo.m_nVertSelected > 0)
				{
					DeletePt(m_mouseInfo.m_nVertSelected-1);
				}
				else if(vecSplinePt[0] > lfHighBound)
				{
					DeletePt(m_mouseInfo.m_nVertSelected);
				}
				else
				{
					m_arrLfSegPositions[m_mouseInfo.m_nVertSelected] = vecSplinePt[0];
					m_arrLfSegHeights[m_mouseInfo.m_nVertSelected] = vecSplinePt[1];
				}
			}
			else
			{
				int nVert = FindNextPoint(vecSplinePt[0]-m_mouseInfo.m_lfPointSelectFuzz);
				if(fabs(m_arrLfSegPositions[nVert]-vecSplinePt[0]) <=
					m_mouseInfo.m_lfPointSelectFuzz)
				{
					m_mouseInfo.m_nVertSelected = nVert;
				}
				else
				{
					m_mouseInfo.m_nVertSelected = 
						InsertPt(vecSplinePt[0], vecSplinePt[1]);
				}
				// pick vertex
			}

			m_mouseInfo.m_bLastMouseValid = true;
		}
		else
		{
			m_mouseInfo.m_bLastMouseValid = false;
		}

		if(nFlags & MK_RBUTTON)
		{
			int nVert = FindNextPoint(vecSplinePt[0]-m_mouseInfo.m_lfPointSelectFuzz);
			if(fabs(m_arrLfSegPositions[nVert]-vecSplinePt[0]) <=
				m_mouseInfo.m_lfPointSelectFuzz)
			{
				DeletePt(nVert);
			}
		}
		if(m_pCallback)
		{
			m_pCallback->DoCallback();
		}
	}
	else
	{
		m_mouseInfo.m_bLastMouseValid = false;
	}
	//this->m_pRayPicker->PickRayFromScreenCoord(
	return false;
}
